package game;

import java.awt.Point;
import java.util.HashSet;

/**
 * Runs the chess game according the the rules of chess on wikipedia.These rules include:
 * the 50 move rule, castling, en passant, promotion, check and checkmate, resigning and 
 * draw.
 * 
 * @author Nicole Waldrum
 * @date January 30, 2010
 * @version 1.0
 *
 * @author Mark Kelly
 * @date February 20, 2010
 * @version 2.0
 */
public class ClassicChess extends GameType {
	private King whiteKing;
	private King blackKing;
	private Team turn;
	/**
	 * Creates a new instance of <tt>ClassicChess</tt>
	 * 
	 * After this constructor returns, <b>setGameState() must be called before any other method is invoked</b>.
	 */
	public ClassicChess() {
		super();
		whiteKing = new King(new Point(5,1), Team.WHITE);
		blackKing = new King(new Point(5,8), Team.BLACK);
		turn = Team.WHITE;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * In the case of <tt>ClassicChess</tt>, do nothing.
	 */
	public void pieceMoved (Piece p) {
		turn = turn.other();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * In the case of <tt>ClassicChess</tt>, 32 Pieces are placed on the board: 16 black and 16 white.
	 */
	@Override
	public void setupBoard() {
		HashSet<Piece> pieces = new HashSet<Piece>();
		
		// White Pieces
		// Pawns
		for (int i = 1; i <=8; i++) {
			pieces.add(new Pawn(new Point(i, 2), Team.WHITE));
		}
		// Rooks
		pieces.add(new Rook(new Point(1,1), Team.WHITE));
		pieces.add(new Rook(new Point(8,1), Team.WHITE));
		// Knights
		pieces.add(new Knight(new Point(2,1), Team.WHITE));
		pieces.add(new Knight(new Point(7,1), Team.WHITE));
		// Bishops
		pieces.add(new Bishop(new Point(3,1), Team.WHITE));
		pieces.add(new Bishop(new Point(6,1), Team.WHITE));
		// Queen
		pieces.add(new Queen(new Point(4,1), Team.WHITE));
		// King
		pieces.add(whiteKing);
		
		// Black Pieces
		// Pawns
		for (int i = 1; i <=8; i++) {
			pieces.add(new Pawn(new Point(i, 7), Team.BLACK));
		}
		// Rooks
		pieces.add(new Rook(new Point(1,8), Team.BLACK));
		pieces.add(new Rook(new Point(8,8), Team.BLACK));
		// Knights
		pieces.add(new Knight(new Point(2,8), Team.BLACK));
		pieces.add(new Knight(new Point(7,8), Team.BLACK));
		// Bishops
		pieces.add(new Bishop(new Point(3,8), Team.BLACK));
		pieces.add(new Bishop(new Point(6,8), Team.BLACK));
		// Queen
		pieces.add(new Queen(new Point(4,8), Team.BLACK));
		// King
		pieces.add(blackKing);
		
		// add all of the Pieces to the board
		state.setBoard(new ChessBoard(pieces));
		
	}

	/**
	 * {@inheritDoc}
	 * 
	 * In the case of <tt>ClassicChess</tt>, a <tt>Piece</tt> can only land on a <tt>Piece</tt> of the opposite colour.
	 */
	public boolean isValidMove(Piece piece, Point dest) {
		ChessBoard board = state.getBoard();
		
		if (piece.getTeam() != turn)
			return false;
		
		if (piece instanceof King) {
			// TODO Castling
			if (isKingInCheck(piece.getTeam(), dest))
				return false;
		}
		else if (piece instanceof Pawn) {
			//TODO en passant
			if (board.hasPieceAt(dest) && board.getPieceAt(dest).getTeam() == piece.getTeam().other()) {
				int dy = (int) ( Math . abs ( piece . getLocation () . getY () - dest . getY()));
				int dx = (int)( dest . getX () - piece . getLocation () . getX ());
				
				if (dy == 1) {
					if (piece.getTeam() == Team.WHITE && dx == 1) {
						return true;
					} else if (piece.getTeam() == Team.BLACK && dx == -1) {
						return true;
					}
				}
				
				return false;
			}
			return piece.isValidMove(piece, dest);
		}
		
		for (Point point : piece.checkMovePattern(dest))
			if (board.hasPieceAt(point))
				return false;
		
		
		return true;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * In the case of <tt>ClassicChess</tt>, the opposing <tt>King</tt> must be in check and unable to escape.
	 */
	public boolean hasWon(Team team) {
		Point  king = (team.equals(Team.BLACK)) ? whiteKing.getLocation() : blackKing.getLocation();
		
		/*
		 * If the King is in check in every space it can move to, checkmate
		 * 
		 * TODO make the big-O less than O(n^bajillion)
		 */
		for (int i = king.x - 1; i <= king.x + 1; i++)
			for (int j = king.y - 1; j <= king.y +1; j++)
				if (isKingInCheck(team, new Point(i, j)))
					// TODO can a piece block the check?
					return true;
					
		return false;
	}
	
	public boolean isKingInCheck(Team team, Point point) {
		Team other = team.other();
				
		for (Piece p : state.getBoard().getAllPieces())
			if (p.getTeam().equals(other) && (p.isValidMove(p, point)))
					return false;
		
		return true;
	}
	
	@Override
	public boolean movePiece(Point start, Point dest) {
		ChessBoard board = state.getBoard();
		if (board.hasPieceAt(start)) {
			Piece p = board.getPieceAt(start);
			// check if its a valid move and if the pawn exception is in effect
			if (state.isValidMove(p, dest) || (p instanceof Pawn && this.isValidMove(p, dest))) {
				// check if you're taking a piece or not
				if (board.hasPieceAt(dest)){
					if (board.getPieceAt(dest).getTeam() != p.getTeam()) {
						board.removePiece(board.getPieceAt(dest));
					} else {
						return false;
					}
				}
				board.movePiece(p, dest);
				state.pieceMoved(p);
				return true;
			}
		}
		// invalid move
		return false;
	}
}